﻿using System;
using System.Reflection;
using System.Xml.Linq;

namespace Comdiv.QWeb.Utils {
	public static class XmlExtensions {
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Global
		public static T apply<T>(this XElement x, T target, params string[] skipattributes) {

			if (null == x) return target;
			skipattributes = skipattributes ?? new string[] {};
			if (Equals(default(T), target)) return target;
			var type = target.GetType();
			foreach (var a in x.Attributes()) {
				var name = a.Name.LocalName;
				var value = a.Value;
				if (-1 != Array.IndexOf(skipattributes, name)) continue;
				var field = type.GetField(name,
				                          BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public |
				                          BindingFlags.NonPublic);
				if (null != field) {
					field.SetValue(target, value.to(field.FieldType));
					continue;
				}
				var property = type.GetProperty(name,
				                                BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public |
				                                BindingFlags.NonPublic | BindingFlags.SetProperty);
				if (null != property && null != property.GetSetMethod(true)) {
					property.SetValue(target, value.to(property.PropertyType), null);
				}
			}
			var customXmlApplyer = target as ICustomXmlApplyer;
			if (customXmlApplyer != null) {
				(customXmlApplyer).Apply(x);
			}
			return target;
		}

		public static ElementDescriptor describe(this XElement e) {
			if (null == e) throw new ArgumentException("element must not be null", "e");
			var descriptor = e.Annotation<ElementDescriptor>();
			if (null == descriptor) {
				descriptor = new ElementDescriptor(e);
				e.AddAnnotation(descriptor);
			}
			return descriptor;
		}

		public static string attr(this XElement e, XName name, string def = "") {
			if (null == e) return def;
			var a = e.Attribute(name);
			return null == a ? def : a.Value;
		}

		public static string id(this XElement e) {
			var attr = e.Attribute("id") ?? e.Attribute("code");
			if (null == attr) return "";
			return attr.Value;
		}


		public static string nameOrValue(this XElement e) {

			var attr = e.Attribute("name");
			if (null != attr) return attr.Value;
			return e.Value;
		}

		#region Nested type: ElementDescriptor
		// ReSharper disable MemberCanBePrivate.Global
		// ReSharper disable UnusedAutoPropertyAccessor.Global
		public class ElementDescriptor {
			public ElementDescriptor(XElement e) {
				Element = e;
				Id = e.id();
				File = e.attr("_file");
				Line = e.attr("_line").toInt();
				Name = e.attr("name");
			}



			public string Name { get; set; }



			public int Line { get; set; }

			public string File { get; set; }

			public string Id { get; set; }

			public XElement Element { get; private set; }

			public string ToWhereString() {
				return string.Format("{0}:{1}", File, Line);
			}
		}
		// ReSharper restore MemberCanBePrivate.Global
		// ReSharper restore UnusedAutoPropertyAccessor.Global
		#endregion

		#region Nested type: ICustomXmlApplyer

// ReSharper disable MemberCanBePrivate.Global
		public interface ICustomXmlApplyer {
// ReSharper restore MemberCanBePrivate.Global
			void Apply(XElement element);
		}

		#endregion
		// ReSharper restore UnusedMember.Global
		// ReSharper restore InconsistentNaming
	}
}